using System;
using System.Collections.Generic;
using GameFramework.Battle.Core;
using Gameplay.PVE.Config;
using Gameplay.PVE.Effect;
using Gameplay.PVE.Entity;
using Gameplay.PVE.Utils;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Gameplay.PVE.Skill
{
    public class TrackBullet : SkillBullet
    {
        //追踪的子弹
        private UnitBase target;
        private bool isHeadShot = false;
        private bool isDamaged;
        private bool isShootObstacle;
        private Action damageEvent;

        private bool isRebound;
        private bool isMissed;
        private bool isShownMissed;
        private Vector3 missOffset;
        private float startMissTime;
        private float missTime;
        private float startReboundTime;

        private bool isInitedThisFrame;

        private PveSkillConfig skillConfig;
        private Vector3 startPosition;
        
        private int restJumpTimes;
        private int trackIndex;

        private HashSet<int> hitUnits = new HashSet<int>();

        public override void Dispose(bool isForceDestroy = false)
        {
            hitUnits.Clear();
            base.Dispose(isForceDestroy);
        }

        public void SetTarget(PveSkillBulletConfig config,UnitBase target)
        {
            if (target == null || source == null)
            {
                return;
            }

            hitUnits.Add(target.Data.id);
            this.target = target;
            if (target is ObstacleUnit)
            {
                trackingPoint = target.gameObject.transform;
                return;
            }
            var list = target.Data.hitPoints;
            trackingPoint = null;
            if (list == null || list.Count == 0)
            {
                Debug.Log(target + target.Data.id.ToString() +" 受击点找不到");
                trackingPoint = target.gameObject.transform;
                return;
            }

            isHeadShot = false;
            isMissed = false;
            
            //计算暴击
            int index = -1;
            if (list != null && list.Count > 0)
            {
                index = Random.Range(1, list.Count);
                if (Random.Range(0, 1f) <= source.Data.CriticalRate)
                {
                    index = 0;
                }
            }
            
            if (PveGlobalVlues.TrackBulletType == 2)
            {
                index = index > 1 ? 1 : index;
            }

            if (index >= list.Count)
            {
                Debug.Log("数组越界 " + source.Data.id + " " + target.Data.id + " " + index);
                index = list.Count - 1;
            }
            if (config.force_hit == 1)
            {
                trackingPoint = list[0];
                index = 0;
            }
            else if (config.force_hit == 2)
            {
                isHeadShot = true;
                trackingPoint = list[0];
            }
            else if (Random.Range(0f,1f) >= source.Data.accuracy - target.Data.evade) //miss
            {
                isMissed = true;
                trackingPoint = list[index];
                target.Transmit(ETransmitType.Evade);
            }
            else if (index == 0)//爆头
            {
                isHeadShot = true;
                trackingPoint = list[index];
            }
            else//正常打击
            {
                trackingPoint = list[index];
            }

            this.trackIndex = index;

            isDamaged = false;
            damageEvent = null;
        }
        private void CheckObstacle(Vector3 startPosition)
        {
            isShootObstacle = false;
            if (config.force_hit == 1 || skill is SkillMelee || source.Data.team == target.Data.team)
            {
                return;
            }

            if (target is ObstacleUnit)
            {
                return;
            }
            
            var obstacle = target.Data.hidingObstacle;
            if (obstacle != null && obstacle.Data.hidingUnit == target && Random.Range(0,1f) < obstacle.Data.obstacleConfig.miss_rate / 10000f)
            {
                var position = obstacle.Data.position + obstacle.Data.colliderCenter +
                               PveUtils.GetRandomVector3(-obstacle.Data.colliderSize / 2,
                                   obstacle.Data.colliderSize / 2);
                var direction = position - startPosition;
                float distance = config.max_distance == 0 ? 20 : config.max_distance;
                var casts = Physics.RaycastAll(startPosition, direction, distance,PveUtils.pveLogicLayer);
                var hitPoint = position;
                for (int i = 0; i < casts.Length; i++)
                {
                    int id;
                    int.TryParse(casts[i].transform.gameObject.name, out id);
                    if (id == obstacle.Data.id)
                    {
                        hitPoint = casts[i].point;
                    }
                }

                isShootObstacle = true;
                obstaclePosition = hitPoint;
            }
        }

        private void GetTrackPointFromObstacleUnit()
        {
            var startPosition = bullet.transform.position;
            var obstaclePosition = target.Data.position + new Vector3(0, 0.7f, 0);
            var direction = obstaclePosition - startPosition;
            var length = 999;
            var casts = Physics.RaycastAll(startPosition, direction, length,PveUtils.pveLogicLayer);
            for (int i = 0; i < casts.Length; i++)
            {
                int id;
                int.TryParse(casts[i].transform.gameObject.name, out id);
                {
                    if (id == target.Data.id)
                    {
                        trackingPosition = casts[i].point;
                        break;
                    }
                }
            }
        }
        
        public override void CheckStartHit()
        {
            if (isMissed)
            {
                return;
            }
            if (trackingPoint != null && target is BattleUnit)
            {
                trackingPosition = trackingPoint.position;
            }

            if (Vector3.Distance(trackingPosition, bullet.transform.position) <= speedValue * TimeManager.LogicDeltaTime)
            {
                if (target is BattleUnit unit)
                {
                    HitUnitWithCrit(target, trackingPoint, isHeadShot);
                }
                else if (target is ObstacleUnit obst)
                {
                    HitObstacle(obst,trackingPosition);
                }

                bullet.transform.position = trackingPosition;
                Dispose();
                return;
            }
            base.CheckStartHit();
        }

        private Transform trackingPoint;
        
        private Vector3 trackingPosition;

        public override void Initialize(PveSkillBulletConfig config, PveSkillConfig skillConfig,SkillBase skill, Vector3 startPosition, Vector3 offset, Vector3 angle,
            UnitBase source)
        {
            restJumpTimes = config.jump_times;
            base.Initialize(config, skillConfig,skill, startPosition, offset, angle, source);
            if (config.force_hit == 1)
            {
                isMissed = false;
            }
            //bullet.name = "bullet " + source.Data.id + " -> " + target.Data.id;    
            isDamaged = false;
            isRebound = false;

            if (target is ObstacleUnit)
            {
                GetTrackPointFromObstacleUnit();
            }
            else if (trackingPoint != null)
            {
                trackingPosition = trackingPoint.transform.position;
            }
            
            
            if (!IsDisposed)
            {
                speed = (trackingPosition - bullet.transform.position).normalized * speedValue;
            }
            isInitedThisFrame = true;
            isShownMissed = false;
            //检查是否要射到障碍物
            CheckObstacle(startPosition);
            //检查是否miss
            if (!isShootObstacle && isMissed && trackingPoint != null)
            {
                var missOffsetDirection = (trackingPosition - source.Data.position).normalized;
                float ratio = Random.Range(0.15f, 0.2f);
                ratio = Random.Range(0, 1f) >= 0.5f ? ratio : -ratio;
                missOffset = Quaternion.AngleAxis(90,Vector3.up) * missOffsetDirection * ratio;
                isShownMissed = false;
            }
            if (isShootObstacle)
            {
                speed = obstaclePosition - bullet.transform.position;
            }
            source.Data.bulletForward = speed;
            bullet.transform.forward = speed;
            lastTrackingPoint = trackingPoint;
            hitUnits = new HashSet<int>();
            hitUnits.Add(target.Data.id);
            //闪电链处理
            /*if (staticBullet != null)
            {
                var chainEffect = staticBullet.GetComponent<ChainEffect>();
                if (chainEffect != null)
                {
                    chainEffect.Play(bullet.transform.position,trackingPoint.transform.position);
                }
            }*/
            //ShowEffect();
        }
        
        private Transform lastTrackingPoint;

        private void ShowJumpEffect()
        {
            int jumpEffectId = config.jump_effect;
            if (jumpEffectId != 0)
            {
                var effect =
                    PveResourceManager.GetResource(PveResourceManager.EPveResType.SkillEffect, config.jump_effect);
                effect.transform.position = bullet.transform.position;
                effect.transform.SetParent(PveScene.pveRootTrans);
                 var chainEffect = effect.GetComponent<ChainEffect>();
                if (chainEffect != null)
                {
                    chainEffect.Play(lastTrackingPoint,trackingPoint);
                }

                TimeManager.Instance.DelayCall(5000,
                    () =>
                    {
                        PveResourceManager.RecoverResource(effect, PveResourceManager.EPveResType.SkillEffect,
                            jumpEffectId);
                    });
            }
        }

        private void Jump()
        {
            UnitBase target = null;
            var targetTeam = this.target.Data.team;
            var teamUnits = UnitManager.Instance.GetAllUnitByTeam(targetTeam);
            float distance = float.MaxValue;
            for (int i = 0; i < teamUnits.Count; i++)
            {
                if (hitUnits.Contains(teamUnits[i].Data.id))
                {
                    continue;
                }
                var d = Vector3.Distance(this.target.Data.position, teamUnits[i].Data.position);
                if (d < distance)
                {
                    target = teamUnits[i];
                    distance = d;
                }
            }

            if (target == null)
            {
                Dispose();
                return;
            }

            bool isCrit = Random.Range(0,1f) < source.Data.CriticalRate - target.Data.CriticalReduce;

            if (isCrit)
            {
                trackIndex = 0;
            }
            else if (target.Data.hitPoints != null)
            {
                trackIndex = Random.Range(1, target.Data.hitPoints.Count);
            }
            
            SetTarget(config,target);
            ResetBullet();
            if (config.force_hit == 1)
            {
                isMissed = false;
            }
            //bullet.name = "bullet " + source.Data.id + " -> " + target.Data.id;    
            isDamaged = false;
            isRebound = false;
            if (!IsDisposed && trackingPoint != null)
            {
                speed = (trackingPoint.position - bullet.transform.position).normalized * speedValue;
            }
            isInitedThisFrame = true;
            isShownMissed = false;
            //检查是否要射到障碍物
            CheckObstacle(startPosition);
            //检查是否miss
            if (!isShootObstacle && isMissed && trackingPoint != null)
            {
                var missOffsetDirection = (trackingPoint.position - source.Data.position).normalized;
                float ratio = Random.Range(0.15f, 0.2f);
                ratio = Random.Range(0, 1f) >= 0.5f ? ratio : -ratio;
                missOffset = Quaternion.AngleAxis(90,Vector3.up) * missOffsetDirection * ratio;
                isShownMissed = false;
            }
            if (isShootObstacle)
            {
                speed = obstaclePosition - bullet.transform.position;
            }
            source.Data.bulletForward = speed;
            ShowJumpEffect();
            lastTrackingPoint = trackingPoint;
        }

        public override void Update(float deltaTime)
        {
            if (IsDisposed)
            {
                return;
            }
            if (isInitedThisFrame)
            {
                isInitedThisFrame = false;
                return;
            }
            lastTime = TimeManager.logicTime;
            if (target != null && !isRebound)
            {
                
                Vector3 targetPosition = target.Data.position + new Vector3(0, 1, 0);
                if (isShootObstacle)
                {
                    targetPosition = obstaclePosition;
                }
                else if (trackingPoint != null)
                {
                    if (target is ObstacleUnit)
                    {
                        
                    }
                    else
                    {
                        trackingPosition = trackingPoint.position;
                    }
                    if (isMissed)
                    {
                        targetPosition = trackingPosition + missOffset;
                    }
                    else
                    {
                        targetPosition = trackingPosition;
                    }
                }

                if (!isShownMissed)
                {
                    this.targetPosition = targetPosition;
                    speed = (targetPosition - lastPosition).normalized * speedValue;
                }

                if (speed != Vector3.zero)
                {
                    bullet.transform.forward = speed;
                }

                CheckHit(lastPosition, speed, speedValue * deltaTime,true);
                
                if (target.Data.isDead && Vector3.Distance(targetPosition, lastPosition) < speedValue)
                {
                    Dispose();
                    return;
                }

                if (isDamaged)
                {
                    damageEvent?.Invoke();
                    if (restJumpTimes > 0)
                    {
                        Jump();
                        restJumpTimes--;
                    }
                    else
                    {
                        Dispose();
                    }
                   
                    return;
                }
            }
            else if (isRebound)
            {
                if (TimeManager.logicTime - startReboundTime > 0.2f)
                {
                    Dispose();
                    return;
                }
                if (isDamaged)
                {
                    damageEvent?.Invoke();
                    damageEvent = null;
                    isDamaged = false;
                }
                bullet.transform.forward = speed;
            }
            UpdatePosition(deltaTime);
            UpdateTrail();
        }

        protected override void UpdatePosition(float deltaTime)
        {
            if (!IsDisposed)
            {
                var diff = targetPosition - lastPosition;
                var speed = this.speed * deltaTime;
                Vector3 currentPosition;
                if (isMissed && isShownMissed)
                {
                    if (diff.sqrMagnitude <= speed.sqrMagnitude)
                    {
                        isDamaged = true;
                        damageEvent = null;
                    }
                    else if (lastPosition.y <= 0)
                    {
                        var position = lastPosition;
                        position.y = 0.01f;
                        PveEffectManager.Instance.PlayEffect((int)ESkillEffect.hitObstacleGround,position,Vector3.forward);
                        isDamaged = true;
                        damageEvent = null;
                    }
                }
                
                if (diff.sqrMagnitude <= speed.sqrMagnitude && !isRebound)
                {
                    currentPosition = targetPosition;
                    if (isMissed)
                    {
                        if (!isShownMissed)
                        {
                            if (!PveManager.Instance.isInPve)
                            {
                                PveFlyTextManager.Instance.ShowMiss(target.Data.flyTextPosition, target.Data.id,
                                    target.Data.team);
                            }
                            isShownMissed = true;
                            targetPosition += speed.normalized * 10;
                        }
                    }
                    else
                    {
                        isDamaged = true;
                        damageEvent = ()=>
                        {
                            if (target is BattleUnit unit)
                            {
                                HitUnitWithCrit(target, trackingPoint, isHeadShot);
                            }
                            else if (target is ObstacleUnit obst)
                            {
                                HitObstacle(obst,trackingPosition);
                            }
                        };
                    }
                    /*HitUnit(target as BattleUnit,trackingPoint,isHeadShot);
                    Dispose();
                    return;*/
                }
                else
                {
                    currentPosition = lastPosition + speed;
                }
                bullet.transform.position = currentPosition;
                lastPosition = currentPosition;
            }
        }

        protected override void CheckHit(Vector3 startPosition,Vector3 direction,float length,bool considerObstacle)
        {
            if (isDamaged || isRebound || isMissed)
            {
                return;
            }

            if (config.force_hit == 1)
            {
                if (Vector3.Distance(target.Data.position, startPosition) < direction.magnitude)
                {
                    if (target is BattleUnit battleUnit)
                    {
                        HitUnitWithCrit(battleUnit, trackingPoint, isHeadShot);
                    }
                    else if (target is ObstacleUnit obstacleUnit)
                    {
                        HitObstacle(obstacleUnit,target.Data.position);
                    }
                    Dispose();
                    return;
                }
            }
            //bool hasHit = false;
            
            
            var casts = Physics.RaycastAll(startPosition, direction, length,PveUtils.pveLogicLayer);
            for (int i = 0; i < casts.Length; i++)
            {
                int id;
                int.TryParse(casts[i].transform.gameObject.name, out id);
                
                {
                    var obstacle = PveManager.Instance.GetObstacleUnit(id);
                    if (obstacle != null && isShootObstacle &&considerObstacle && obstacle.Data.hidingUnit != source)
                    {
                        isDamaged = true;
                        damageEvent = () =>
                        {
                            HitObstacle(obstacle,casts[i].point);
                        };
                        bullet.transform.position = targetPosition;
                        break;
                    }
                }
            }
        }
       
    }
}